package cn.kbyue.security.security;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 自定义用户认证
 *
 * @author xl
 * @date 2020/4/19 15:25
 */
@Slf4j
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Resource
    private UserDetailsService userDetailsService;

    /**
     * 自定义用户登录检测
     * @param authentication 用户登录信息
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String username = (String) authentication.getPrincipal();
        String password = (String) authentication.getCredentials();
        // 查询用户信息
        DefinedUserDetails userDetails = (DefinedUserDetails) userDetailsService.loadUserByUsername(username);
        boolean matches = bCryptPasswordEncoder.matches(password, userDetails.getPassword());
        if (!matches) {
            throw new BadCredentialsException("密码错误");
        }
        // 检查用户状态是否异常，否则抛出异常
        checkUserStatus(userDetails);
        return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return true;
    }

    /**
     * 抛出账户的异常状态
     * @param userDetails DefinedUserDetails
     */
    private void checkUserStatus(DefinedUserDetails userDetails) {
        if (!userDetails.isEnabled()) {
            throw new DisabledException("账户被禁用");
        } else if (!userDetails.isAccountNonLocked()) {
            throw new LockedException("账户被锁定");
        } else if (!userDetails.isAccountNonExpired()) {
            throw new AccountExpiredException("账户已经过期");
        }
    }
}

